using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using Microsoft.Dss.Core;
using Microsoft.Dss.Core.Attributes;

namespace VEXRobotics
{
    [DataContract]
    public class PortSensorData
    {
        private SensorDefinition.SensorType _type = SensorDefinition.SensorType.None;
        private ushort _value = 0;

        /// <summary>
        /// Type of sensor
        /// </summary>
        [DataMember]
        public SensorDefinition.SensorType Type
        {
            get { return _type; }
            set { _type = value; }
        }

        /// <summary>
        /// Value for the IO port
        /// </summary>
        [DataMember]
        public ushort Value
        {
            get 
            {
                switch (Type)
                {
                        // Analog values return the full range
                    case SensorDefinition.SensorType.AnalogInput:
                    case SensorDefinition.SensorType.LightSensor:
                    case SensorDefinition.SensorType.Sonar:
                    case SensorDefinition.SensorType.Potentiometer:
                    case SensorDefinition.SensorType.Gyro:
                    case SensorDefinition.SensorType.Accelerometer:
                    case SensorDefinition.SensorType.LineTracker:
                        return _value;

                        // Digital values are returned as 0 or '-1'
                    case SensorDefinition.SensorType.Button:
                    case SensorDefinition.SensorType.DigitalInput:
                    case SensorDefinition.SensorType.DigitalOutput:
                    case SensorDefinition.SensorType.Encoder:
                    case SensorDefinition.SensorType.Limit:
                        {
                            if (_value != 0)
                                return 0xffff;
                            return 0;
                        }
                }
                return 0;
            }
            set
            {
                switch (Type)
                {
                        // Analog values are set by full value
                    case SensorDefinition.SensorType.AnalogInput:
                    case SensorDefinition.SensorType.LightSensor:
                    case SensorDefinition.SensorType.Sonar:
                    case SensorDefinition.SensorType.Potentiometer:
                    case SensorDefinition.SensorType.Gyro:
                    case SensorDefinition.SensorType.Accelerometer:
                    case SensorDefinition.SensorType.LineTracker:
                        {
                            _value = value;
                        }
                        break;

                        // Digital values are zero or non-zero
                    case SensorDefinition.SensorType.Button:
                    case SensorDefinition.SensorType.DigitalInput:
                    case SensorDefinition.SensorType.DigitalOutput:
                    case SensorDefinition.SensorType.Encoder:
                    case SensorDefinition.SensorType.Limit:
                        {
                            if (value != 0)
                                _value = 0xffff;
                            else
                                _value = 0;
                            break;
                        }
                }
            }
        }
    }

    /// <summary>
    /// A bank of sensors
    /// </summary>
    [DataContract()]
    public class SensorConfig : IDssSerializable, ICloneable
    {
        PortSensorData[] _sensor = new PortSensorData[16];
        private uint _changes;

        /// <summary>
        /// Default constructor
        /// </summary>
        public SensorConfig()
        {
            _sensor[0] = new PortSensorData();
            _sensor[1] = new PortSensorData();
            _sensor[2] = new PortSensorData();
            _sensor[3] = new PortSensorData();
            _sensor[4] = new PortSensorData();
            _sensor[5] = new PortSensorData();
            _sensor[6] = new PortSensorData();
            _sensor[7] = new PortSensorData();
            _sensor[8] = new PortSensorData();
            _sensor[9] = new PortSensorData();
            _sensor[10] = new PortSensorData();
            _sensor[11] = new PortSensorData();
            _sensor[12] = new PortSensorData();
            _sensor[13] = new PortSensorData();
            _sensor[14] = new PortSensorData();
            _sensor[15] = new PortSensorData();
        }

        #region IO port sensor definition

        /// <summary>
        /// Sensor array
        /// </summary>
       [DataMember()]
       public PortSensorData[] Sensors
        {
            get { return this._sensor; }
            set { this._sensor = value; }
        }

        #endregion

        #region Analog Inputs

        /// <summary>
        /// Set the value for a specific port
        /// </summary>
        /// <param name="port">Port to set (0 based)</param>
        /// <param name="value">Value to set the port to</param>
        public void SetAnalogValue(int port, ushort value)
        {
            _sensor[port].Value = value;
        }

        /// <summary>
        /// Analog sensor array
        /// </summary>
       [DataMember()]
       public ushort[] AnalogValues
        {
            get
            {
                ushort[] value = new ushort[16];
                int i;
                for (i = 0; i < 16; ++i)
                {
                    value[i] = _sensor[i].Value;
                }
                return value;
            }
            set
            {
                int i;
                for (i = 0; i < value.Length && i < 16; ++i)
                {
                    _sensor[i].Value = value[i];
                }
            }
        }

        #endregion

        #region Binary Inputs

        /// <summary>
        /// Set the value of a binary port
        /// </summary>
        /// <param name="port">Port to set (0 based)</param>
        /// <param name="value">Value to set</param>
        public void SetBinaryValue(int port, bool value)
        {
            _sensor[port].Value = (ushort) (value ? 1 : 0);
        }

        /// <summary>
        /// Binary sensor array
        /// </summary>
       [DataMember()]
       public bool[] BinaryValues
        {
            get
            {
                bool[] binValue = new bool[16];
                int i;
                for (i = 0; i < 16; ++i)
                {
                    binValue[i] = _sensor[i].Value != 0 ? true : false;
                }
                return binValue;
            }
            set
            {
                int i;
                for (i = 0; i < value.Length && i < 16; ++i)
                {
                    _sensor[i].Value = (ushort) (value[i] ? 1 : 0);
                }
            }
        }

        #endregion

        #region Change Flags

        /// <summary>
        /// One bit set for every sensor that changed since last time slice
        /// </summary>
       [DataMember()]
       public uint Changes
        {
            get { return this._changes; }
            set { this._changes = value; }
        }

        [Flags]
        public enum Mask : uint
        {
            Change1 = 0x00000001,
            Change2 = 0x00000002,
            Change3 = 0x00000004,
            Change4 = 0x00000008,
            Change5 = 0x00000010,
            Change6 = 0x00000020,
            Change7 = 0x00000040,
            Change8 = 0x00000080,
            Change9 = 0x00000100,
            Change10 = 0x00000200,
            Change11 = 0x00000400,
            Change12 = 0x00000800,
            Change13 = 0x00001000,
            Change14 = 0x00002000,
            Change15 = 0x00004000,
            Change16 = 0x00008000

        }

        /// <summary>
        /// Set a specific sensor has having changed
        /// </summary>
        public void SetChanged(int port)
        {
            _changes |= (uint)(1 << port);
        }

        #endregion


        #region ICloneable Members

        /// <summary>
        /// Define how to clone ourselves
        /// </summary>
        /// <returns></returns>
        public object Clone()
        {
            SensorConfig config = new SensorConfig();
            for (int i = 0; i < _sensor.Length; ++i)
            {
                config.AnalogValues[i] = this.AnalogValues[i];
                config.BinaryValues[i] = this.BinaryValues[i];
                config.Sensors[i].Type = this.Sensors[i].Type;
                config.Sensors[i].Value = this.Sensors[i].Value;
            }
            config.Changes = config.Changes;
            return config;
        }

        #endregion

        #region IDssSerializable

        /// <summary>
        /// Copy To state
        /// </summary>
        public virtual void CopyTo(IDssSerializable target)
        {
            SensorConfig typedTarget = target as SensorConfig;

            if (typedTarget == null)
                throw new ArgumentException("CopyTo({0}) requires type {0}", this.GetType().FullName);

            for(int i=0; i<this.Sensors.Length; ++i)
            {
                typedTarget.AnalogValues[i] = this.AnalogValues[i];
                typedTarget.BinaryValues[i] = this.BinaryValues[i];
                typedTarget.Sensors[i].Type = this.Sensors[i].Type;
                typedTarget.Sensors[i].Value = this.Sensors[i].Value;
            }
            typedTarget.Changes = this.Changes;
        }

        /// <summary>
        /// Serialize
        /// </summary>
        public virtual void Serialize(System.IO.BinaryWriter writer)
        {
            for (int i = 0; i < this.Sensors.Length; ++i)
            {
                writer.Write(this.AnalogValues[i]);
                writer.Write(this.BinaryValues[i]);
                writer.Write((int)this.Sensors[i].Type);
                writer.Write(this.Sensors[i].Value);
            }
            writer.Write(this.Changes);
        }

        /// <summary>
        /// Deserialize
        /// </summary>
        public virtual object Deserialize(System.IO.BinaryReader reader)
        {
            for (int i = 0; i < this.Sensors.Length; ++i)
            {
                this.AnalogValues[i] = reader.ReadUInt16();
                this.BinaryValues[i] = reader.ReadBoolean();
                this.Sensors[i].Type = (SensorDefinition.SensorType)reader.ReadInt32();
                this.Sensors[i].Value = reader.ReadUInt16();
            }
            this.Changes = reader.ReadUInt32();

            return this;
        }
        #endregion
    }
}
